package xginplus

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"time"
)

// 分段文件下载时，需要使用此结构进行信息封装
type DFS struct {
	Name        string        // 文件名
	Size        int64         // 文件大小
	Path        []string      // 文件路径
	CacheTime   time.Duration // 缓存时间
	ContentType string        // 文件类型，默认为application/octet-stream
}

// 下载文件【读取分片后的文件列表进行下载】
//
//	inf	待下载的文件结构信息
func (c *Context) DownloadFs(inf *DFS) *Response {
	if inf == nil || len(inf.Path) == 0 {
		return c.Errorf("可下载内容为空")
	}
	// 存放文件打开的句柄
	opts := []*os.File{}
	// 批量关闭
	defer func() {
		for i := 0; i < len(opts); i++ {
			opts[i].Close()
		}
	}()
	if inf.ContentType != "" {
		c.Writer.Header().Set("Content-Type", inf.ContentType)
	} else {
		c.Writer.Header().Set("Content-Type", "application/octet-stream")
	}
	if inf.Name != "" {
		c.Writer.Header().Set("Content-Disposition", "attachment;filename="+inf.Name)
	} else {
		c.Writer.Header().Set("Content-Disposition", "attachment;filename="+time.Now().Format(time.DateTime)+".dat")
	}
	if inf.CacheTime > 0 {
		c.Header("Cache-Control", fmt.Sprintf("max-age=%d, public", int(inf.CacheTime.Seconds())))
	}
	c.Header("Accept-Ranges", "bytes")
	// 打开文件
	sadd := inf.Size == 0
	for i := 0; i < len(inf.Path); i++ {
		opt, err := os.Open(inf.Path[i])
		if err != nil {
			return c.Error(err)
		}
		opts = append(opts, opt)
		if sadd {
			t_info, _ := opt.Stat()
			inf.Size += t_info.Size()
		}
	}
	// 获取Range请求
	httpRange := c.GetHeader("Range")
	if httpRange == "" {
		c.Header("Content-Length", fmt.Sprintf("%d", inf.Size))
		for i := 0; i < len(opts); i++ {
			io.Copy(c.Writer, opts[i])
		}
		return nil
	}
	// 处理Range请求
	var start, end int64
	_, err := fmt.Sscanf(httpRange, "bytes=%d-%d", &start, &end)
	if err != nil || start > end {
		c.Status(http.StatusRequestedRangeNotSatisfiable)
		return nil
	}
	if end == 0 {
		end = inf.Size - 1
	}
	c.Header("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, inf.Size))
	c.Status(http.StatusPartialContent)
	for i := 0; i < len(opts); i++ {
		buffer := make([]byte, end-start+1)
		n, err := opts[i].ReadAt(buffer, start)
		if err != nil && err != io.EOF {
			c.Status(http.StatusInternalServerError)
			return nil
		}
		c.Writer.Write(buffer[:n])
		start += int64(n)
	}
	return nil
}
